package com.ansion.utils.thread;

import com.ansion.log.VLog;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;


/***
 * @author john 2022/11/24
 * 参照CommonUICache的diff串行计算思想，确保任务串行执行，
 * 当正在执行任务时候如果来了新的请求则进行计数，任务执行结束后再执行任务并进行步进，
 * 直到requestStep==lastSubmitStep。
 * 适用于频繁刷新的任务，只需要保留最后的结果。
 */
public class BaseSerialTask {
    private volatile int requestStep, lastSubmitStep;
    private volatile long lastSubmitTime;
    private final AtomicBoolean isRunning = new AtomicBoolean(false);


    /***
     * 这个方法是对外暴露的执行任务的方法
     */
    public void start() {
        requestStep++;
        VLog.d("TAG_SERIALTASK", "asyncExecute %s,requestStep:%d,isRunning:%b", getTaskTag(), requestStep, isRunning);
        realAsyncExecuteTask();

    }


    /***
     * 这个方法是真正想要执行的任务
     */
    protected void executeTask() {

    }


    public String getTaskTag() {
        return "SerialTask";
    }

    private final Runnable task = new Runnable() {
        @Override
        public void run() {
            try {
                VLog.d("TAG_SERIALTASK", "executeTask start for %s,[%d,%d]", getTaskTag(), requestStep, lastSubmitStep);
                executeTask();
                VLog.d("TAG_SERIALTASK", "executeTask end for %s,[%d,%d]", getTaskTag(), requestStep, lastSubmitStep);
            } catch (Throwable e) {
                VLog.e("TAG_SERIALTASK", "executeTask error for %s", e.getMessage());
            } finally {
                synchronized (isRunning) {
                    isRunning.set(false);
                    long duration = System.currentTimeMillis() - lastSubmitTime;
                    VLog.d("TAG_SERIALTASK", "execute %s finish,cost:%d,[%d,%d]", getTaskTag(), duration, requestStep, lastSubmitStep);
                    if (requestStep != lastSubmitStep)
                        executeNextStep();
                }
            }

        }
    };

    private void executeNextStep() {
        VLog.d("TAG_SERIALTASK", "executeNextStep for %s", getTaskTag());
        realAsyncExecuteTask();
    }

    private void realAsyncExecuteTask() {
        synchronized (isRunning) {
            if (!isRunning.get()) {
                lastSubmitStep = requestStep;
                lastSubmitTime = System.currentTimeMillis();
                getExecutor().execute(task);
                isRunning.set(true);
            }
        }
    }


    protected ThreadPoolExecutor getExecutor() {
        return ThreadManager.getInstance().getIo();
    }


}
